﻿using Engine.Graphics;
using SharpDX;
using System;
using System.IO;

namespace Engine.ObjectManager
{
    public class BaseNode : IDisposable
    {
        #region Private Fields

        private MeshObject.BoundingBox boundingBox;
        private Engine engine;
        private float heightOverTerrain;
        private bool locked;
        private Vector3 position;
        private object properties;
        private bool setToTerrain;

        #endregion Private Fields

        #region Public Constructors

        public BaseNode(Engine engine, Vector3 position)
        {
            this.engine = engine;
            this.position = position;
            this.boundingBox = new MeshObject.BoundingBox(this.engine, this.position, 50.0f);
            this.locked = false;
            this.setToTerrain = false;
        }

        #endregion Public Constructors

        #region Public Properties

        public MeshObject.BoundingBox BoundingBox
        {
            get { return boundingBox; }
            set { boundingBox = value; }
        }

        public Engine Engine
        {
            get { return this.engine; }
        }

        public float HeightOverTerrain
        {
            get { return heightOverTerrain; }
            set { heightOverTerrain = value; }
        }

        public bool Locked
        {
            get { return locked; }
            set { locked = value; }
        }

        public Vector3 Position
        {
            get { return position; }
            set
            {
                position = value;
            }
        }

        public object Properties
        {
            get { return properties; }
            set { properties = value; }
        }

        public bool SetToTerrain
        {
            get { return setToTerrain; }
            set { setToTerrain = value; }
        }

        #endregion Public Properties

        #region Public Methods

        public static BaseNode FromStream(BinaryReader br, Engine engine)
        {
            string ident = new string(br.ReadChars(2));
            if (ident == "BN")
            {
                switch (br.ReadInt16()) // Version number
                {
                    case 1:
                        return LoadV1(br, engine);

                    case 2:
                        return LoadV2(br, engine);
                }
            }
            throw new Exception("Unsupported BaseNode format!");
        }

        public void Dispose()
        {
            if (this.boundingBox != null)
                this.boundingBox.Dispose();
            this.boundingBox = null;
        }

        public bool Intersect(Vector3 vNear, Vector3 vFar, out Vector3 cuttingPoint, bool updateVertexBuffer)
        {
            return this.boundingBox.Intersect(vNear, vFar, out cuttingPoint, updateVertexBuffer);
        }

        #endregion Public Methods

        #region Internal Methods

        internal void ToStream(BinaryWriter bw)
        {
            bw.Write("BN".ToCharArray());
            bw.Write((short)2); // VersionNumber
            bw.Write(this.position.X);
            bw.Write(this.position.Y);
            bw.Write(this.position.Z);
            bw.Write(this.setToTerrain);
            bw.Write(this.heightOverTerrain);
        }

        #endregion Internal Methods

        #region Private Methods

        private static BaseNode LoadV1(BinaryReader br, Engine engine)
        {
            BaseNode baseNode = new BaseNode(engine, new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()));

            baseNode.setToTerrain = true;
            baseNode.heightOverTerrain = 0.0f;

            return baseNode;
        }

        private static BaseNode LoadV2(BinaryReader br, Engine engine)
        {
            BaseNode baseNode = new BaseNode(engine, new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()));

            baseNode.setToTerrain = br.ReadBoolean();
            baseNode.heightOverTerrain = br.ReadSingle();

            return baseNode;
        }

        #endregion Private Methods
    }

    public class TestBaseNode : BaseNode, IRenderableObject
    {
        #region Public Constructors

        public TestBaseNode(Engine engine)
            : base(engine, Vector3.Zero)
        {
        }

        #endregion Public Constructors

        #region Public Methods

        public new static BaseNode FromStream(BinaryReader br, Engine engine)
        {
            TestBaseNode testBaseNode = new TestBaseNode(engine);
            Console.WriteLine("TestBaseNode");

            return testBaseNode;
        }

        public new void Dispose()
        {
            if (this.BoundingBox != null)
                this.BoundingBox.Dispose();
            base.Dispose();
        }

        public int GetPriorityLevel()
        {
            return 50;
        }

        public void Render(int renderStep)
        {
            this.BoundingBox.Render(renderStep);
        }

        #endregion Public Methods
    }
}